﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using ServiceStack.OrmLite;

namespace wsy.ext.servicestack.ormlite
{
    public static class Ext
    {
        private static void _InsertAllFix<T>(this IDbConnection db, IEnumerable<T> objs, ref List<T> errors,
            ref IProgress<int> progress, ref int complate, ref Action<Exception> errorexp)
        {
            if (objs == null) return;
            IEnumerable<T> enumerable = objs as T[] ?? objs.ToArray();
            var length = enumerable.Count();
            var first = length / 2;
            var second = length - first;
            if (first == 0)
            {
                var currentobj = enumerable.First();
                try
                {
                    db.Insert(currentobj);
                    complate++;
                    progress?.Report(complate);
                }
                catch (Exception e)
                {
                    errorexp?.Invoke(e);
                    errors.Add(currentobj);
                }
                return;
            }

            if (first == 1 && second == 1)
            {
                var currentobj = enumerable.First();
                try
                {
                    db.Insert(currentobj);
                    complate++;
                    progress?.Report(complate);
                }
                catch (Exception e)
                {
                    errorexp?.Invoke(e);
                    errors.Add(currentobj);
                }

                currentobj = enumerable.Last();
                try
                {
                    db.Insert(currentobj);
                    complate++;
                    progress?.Report(complate);
                }
                catch (Exception e)
                {
                    errorexp?.Invoke(e);
                    errors.Add(currentobj);
                }

                return;
            }

            if (first == 1 && second > 1)
            {
                var currentobj = enumerable.First();
                try
                {
                    db.Insert(currentobj);
                    complate++;
                    progress?.Report(complate);
                }
                catch (Exception e)
                {
                    errorexp?.Invoke(e);
                    errors.Add(currentobj);
                }

                try
                {
                    db.InsertAll(enumerable.Skip(first).Take(second));
                    complate = complate + second - first;
                    progress?.Report(complate);
                }
                catch (Exception e)
                {
                    errorexp?.Invoke(e);
                    db._InsertAllFix(enumerable.Skip(first).Take(second), ref errors, ref progress, ref complate,
                        ref errorexp);
                }

                return;
            }


            try
            {
                db.InsertAll(enumerable.Take(first));
                complate = complate + first;
                progress?.Report(complate);
            }
            catch (Exception e)
            {
                errorexp?.Invoke(e);
                db._InsertAllFix(enumerable.Take(first), ref errors, ref progress, ref complate, ref errorexp);
            }

            try
            {
                db.InsertAll(enumerable.Skip(first).Take(second));
                complate = complate + second - first;
                progress?.Report(complate);
            }
            catch (Exception e)
            {
                errorexp?.Invoke(e);
                db._InsertAllFix(enumerable.Skip(first).Take(second), ref errors, ref progress, ref complate,
                    ref errorexp);
            }
        }

        public static void InsertAllFix<T>(this IDbConnection db, IEnumerable<T> objs, ref List<T> errors,
            int pageSize = 1000, IProgress<int> progress = null, Action<Exception> errorexp = null)
        {
            var complated = 0;
            IEnumerable<T> enumerable = objs as T[] ?? objs.ToArray();
            var length = enumerable.Count();
            var num = length / pageSize;
            for (var i = 0; i < num; i++)
                try
                {
                    db.InsertAll(enumerable.Skip(i * pageSize).Take(pageSize));
                    complated += pageSize;
                    progress?.Report(complated);
                }
                catch (Exception e)
                {
                    errorexp?.Invoke(e);
                    db._InsertAllFix(enumerable.Skip(i * pageSize).Take(pageSize), ref errors, ref progress,
                        ref complated, ref errorexp);
                }

            try
            {
                db.InsertAll(enumerable.Skip(num * pageSize).Take(length - num * pageSize));
                complated += pageSize;
                progress?.Report(complated);
            }
            catch (Exception e)
            {
                errorexp?.Invoke(e);
                db._InsertAllFix(enumerable.Skip(num * pageSize).Take(length - num * pageSize), ref errors,
                    ref progress, ref complated, ref errorexp);
            }
        }

        public static void InsertAllFixIgnoreError<T>(this IDbConnection db, IEnumerable<T> objs, int pageSize = 1000)
        {
            var error = new List<T>();
            InsertAllFix(db, objs, ref error, pageSize);
            error.Clear();
        }
    }
}